package com.hilinos.gb.gb28181.cmd.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.sip.SipFactory;
import javax.sip.address.Address;
import javax.sip.address.SipURI;
import javax.sip.header.CSeqHeader;
import javax.sip.header.CallIdHeader;
import javax.sip.header.FromHeader;
import javax.sip.header.MaxForwardsHeader;
import javax.sip.header.ToHeader;
import javax.sip.header.ViaHeader;
import javax.sip.message.Request;

import com.hilinos.gb.gb28181.cmd.ICommand;
import com.hilinos.gb.gb28181.config.SipConfig;
import com.hilinos.gb.gb28181.processor.request.RequestBuilder;
import com.hilinos.gb.gb28181.repository.mysql.mapper.po.Device;
import com.hilinos.gb.gb28181.repository.mysql.mapper.po.Host;
import com.hilinos.gb.gb28181.utils.DateUtil;
import com.hilinos.gb.gb28181.utils.SsrcUtils;
import com.hilinos.gb.gb28181.utils.UdpClient;
import com.hilinos.hilincommon.utils.AssertUtils;
import com.hilinos.hilincommon.utils.RandomUtils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import gov.nist.javax.sip.address.SipUri;
import gov.nist.javax.sip.header.CallID;
import lombok.SneakyThrows;

@Component
public class SipCommand implements ICommand {

    private static final String RK_SSRC_HOST = "com:hilin:gb:gb28181:ssrc:host:%s";

    @Autowired
    private RequestBuilder requestBuilder;

    @Autowired
    private SipConfig sipConfig;

    @Autowired
    private RedisTemplate<String, Host> redisTemplate;

    @Override
    public boolean deviceInfoQuery(Device device) {
        try {
            String catalogXml = "<?xml version=\"1.0\" encoding=\"GB2312\"?>" +
                    "<Query>" +
                    "<CmdType>DeviceInfo</CmdType>" +
                    "<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>" +
                    "<DeviceID>" + device.getDeviceId() + "</DeviceID>" +
                    "</Query>";
            Request request = requestBuilder.createMessageRequest(device, catalogXml, "ViaDeviceInfoBranch", "FromDeviceInfoTag", "ToDeviceInfoTag");
            transmitRequest(device.getHost(), request);

        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    @Override
    public boolean catalogQuery(Device deviceVo) {

        String catalogXml = "<?xml version=\"1.0\" encoding=\"GB2312\"?>" +
                "<Query>" +
                "<CmdType>Catalog</CmdType>" +
                "<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>" +
                "<DeviceID>" + deviceVo.getDeviceId() + "</DeviceID>" +
                "</Query>";
        Request request = requestBuilder.createMessageRequest(deviceVo, catalogXml, "ViaCatalogBranch", "FromCatalogTag", "ToCatalogTag");
        transmitRequest(deviceVo.getHost(), request);
        return true;
    }

    @Override
    public boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime) {
        try {
            StringBuffer recordInfoXml = new StringBuffer(200);
            recordInfoXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>");
            recordInfoXml.append("<Query>");
            recordInfoXml.append("<CmdType>RecordInfo</CmdType>");
            recordInfoXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>");
            recordInfoXml.append("<DeviceID>" + channelId + "</DeviceID>");
            recordInfoXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "</StartTime>");
            recordInfoXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "</EndTime>");
            recordInfoXml.append("<Secrecy>0</Secrecy>");
            // 大华NVR要求必须增加一个值为all的文本元素节点Type
            recordInfoXml.append("<Type>all</Type>");
            recordInfoXml.append("</Query>");

            Request request = requestBuilder.createMessageRequest(device, recordInfoXml.toString(), "ViaRecordInfoBranch", "FromRecordInfoTag", "ToRecordInfoTag");
            transmitRequest(device.getHost(), request);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    @Override
    public String playStreamCmd(Device device, String channelId) {
        try {
            String ssrc = SsrcUtils.getPlaySsrc();
            String transport = device.getTransport();
            //
            StringBuilder content = new StringBuilder(200);
            content.append("v=0\r\n");
            content.append("o=").append(channelId).append(" 0 0 IN IP4 ").append(sipConfig.getSipIp()).append("\r\n");
            content.append("s=Play\r\n");
            content.append("c=IN IP4 ").append(sipConfig.getMediaIp()).append("\r\n");
            content.append("t=0 0\r\n");
            if ("TCP".equals(transport)) {
                content.append("m=video ").append(sipConfig.getMediaPort()).append(" TCP/RTP/AVP 96 98 97\r\n");
            }
            if ("UDP".equals(transport)) {
                content.append("m=video ").append(sipConfig.getMediaPort()).append(" RTP/AVP 96 98 97\r\n");
            }
            content.append("a=recvonly\r\n");
            content.append("a=rtpmap:96 PS/90000\r\n");
            content.append("a=rtpmap:98 H264/90000\r\n");
            content.append("a=rtpmap:97 MPEG4/90000\r\n");
            if ("TCP".equals(transport)) {
                content.append("a=setup:passive\r\n");
                content.append("a=connection:new\r\n");
            }
            content.append("y=").append(ssrc).append("\r\n");
            Request request = requestBuilder.createInviteRequest(device, channelId, content.toString(), null, "live");
            transmitRequest(device.getHost(), request);
            redisTemplate.opsForValue().set(String.format(RK_SSRC_HOST, ssrc), device.getHost());
            return ssrc;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public String playbackStreamCmd(Device device, String channelId, String startTime, String endTime) {
        try {

            String ssrc = SsrcUtils.getPlaySsrc();
            //
            StringBuilder content = new StringBuilder(200);
            content.append("v=0\r\n");
            content.append("o=").append(sipConfig.getSipId()).append(" 0 0 IN IP4 ").append(sipConfig.getSipIp()).append("\r\n");
            content.append("s=Playback\r\n");
            content.append("u=").append(channelId).append(":0\r\n");
            content.append("c=IN IP4 ").append(sipConfig.getMediaIp()).append("\r\n");
            content.append("t=").append(DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime)).append(" ").append(DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime)).append("\r\n");
            if (device.getTransport().equals("TCP")) {
                content.append("m=video ").append(sipConfig.getMediaPort()).append(" TCP/RTP/AVP 96 98 97\r\n");
            }
            if (device.getTransport().equals("UDP")) {
                content.append("m=video ").append(sipConfig.getMediaPort()).append(" RTP/AVP 96 98 97\r\n");
            }
            content.append("a=recvonly\r\n");
            content.append("a=rtpmap:96 PS/90000\r\n");
            content.append("a=rtpmap:98 H264/90000\r\n");
            content.append("a=rtpmap:97 MPEG4/90000\r\n");
            if (device.getTransport().equals("TCP")) {
                content.append("a=setup:passive\r\n");
                content.append("a=connection:new\r\n");
            }
            content.append("y=").append(ssrc).append("\r\n");//ssrc
            Request request = requestBuilder.createPlaybackInviteRequest(device, channelId, content.toString(), null, "playback");
            transmitRequest(device.getHost(), request);
            redisTemplate.opsForValue().set(String.format(RK_SSRC_HOST, ssrc), device.getHost());
            return ssrc;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public void streamByeCmd(String ssrc) {
        try {
            Host host = redisTemplate.opsForValue().get(String.format(RK_SSRC_HOST, ssrc));
            Request request = requestBuilder.createByeRequest(host, null, null, "playback");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void transmitRequest(Host host, Request request) {
        AssertUtils.isNotEmpty(host, "设备host为空");
        AssertUtils.isNotEmpty(host.getIp(), "设备IP为空");
        AssertUtils.isNotEmpty(host.getPort(), "设备port为空");
        UdpClient.send(request.toString().getBytes(), host);
    }

    @Override
    public List<String> dealErrorOnlineDevice(List<Device> onlineDevices) {
        List<String> offlineDeviceIds = new ArrayList<>();

        if (!ObjectUtils.isEmpty(onlineDevices)) {
            for (Device onlineDevice : onlineDevices) {
                Date heartbeatTime = onlineDevice.getHeartbeatTime();
                if (heartbeatTime == null) {
                    continue;
                }
                if (heartbeatTime.getTime() + sipConfig.getSipHeartbeatTimeout() * 1000L <= System.currentTimeMillis()) {
                    offlineDeviceIds.add(onlineDevice.getDeviceId());
                }
            }
        }

        return offlineDeviceIds;
    }
}
